package blockchain

import (
	"context"
	"fmt"
	"math/big"
	"strings"
	"time"

	"private-domain-overseas-service/admin/business/api/internal/repository/blockchain"
	"private-domain-overseas-service/admin/business/api/internal/repository/integral"
	"private-domain-overseas-service/admin/business/api/internal/svc"
	"private-domain-overseas-service/admin/business/api/internal/types"
	mycommon "private-domain-overseas-service/pkg/common"
	"private-domain-overseas-service/pkg/constant"
	"private-domain-overseas-service/pkg/status/bizblockchain"
	"private-domain-overseas-service/pkg/status/bizintegral"
	"private-domain-overseas-service/tools/ent"
	"private-domain-overseas-service/tools/ent/hzblockchainwalletrecord"
	"private-domain-overseas-service/tools/ent/hzintegralwallet"
	"private-domain-overseas-service/tools/ent/predicate"

	"github.com/ethereum/go-ethereum"
	"github.com/ethereum/go-ethereum/accounts/abi"
	"github.com/ethereum/go-ethereum/common"
	"github.com/ethereum/go-ethereum/ethclient"
	"github.com/pkg/errors"
	"github.com/zeromicro/go-zero/core/logx"
)

type BlockchainApprovalExecuteLogic struct {
	logx.Logger
	ctx    context.Context
	svcCtx *svc.ServiceContext
}

// 后台审批接口
func NewBlockchainApprovalExecuteLogic(ctx context.Context, svcCtx *svc.ServiceContext) *BlockchainApprovalExecuteLogic {
	return &BlockchainApprovalExecuteLogic{
		Logger: logx.WithContext(ctx),
		ctx:    ctx,
		svcCtx: svcCtx,
	}
}

// 检查钱包余额
func (l *BlockchainApprovalExecuteLogic) checkWalletBalance(record *ent.HzBlockchainWalletRecord) (bool, error) {
	// 连接到zkSync节点
	if len(l.svcCtx.Config.Blockchain.GethRpcURLs) == 0 {
		return false, errors.New("没有配置 RPC 节点地址")
	}
	logx.Infof("正在连接RPC节点: %s", l.svcCtx.Config.Blockchain.GethRpcURLs[0])
	client, err := ethclient.Dial(l.svcCtx.Config.Blockchain.GethRpcURLs[0])
	if err != nil {
		return false, errors.Wrap(err, "连接zkSync节点失败")
	}
	defer client.Close()

	// 定义钱包地址和代币合约地址
	walletAddress := common.HexToAddress(l.svcCtx.Config.Blockchain.BackendAddress)
	tokenAddress := common.HexToAddress(l.svcCtx.Config.Blockchain.HzbContract)
	logx.Infof("钱包地址: %s, 代币合约地址: %s", walletAddress.Hex(), tokenAddress.Hex())

	// 验证合约地址是否有效
	code, err := client.CodeAt(l.ctx, tokenAddress, nil)
	if err != nil {
		return false, errors.Wrap(err, "验证合约地址失败")
	}
	if len(code) == 0 {
		return false, errors.New("合约地址无效或合约未部署")
	}

	// ERC-20 ABI（只需要balanceOf函数的部分）
	erc20ABI := constant.Erc20ABI

	// 解析ABI
	parsedABI, err := abi.JSON(strings.NewReader(erc20ABI))
	if err != nil {
		return false, errors.Wrap(err, "解析ABI失败")
	}

	// 构造调用数据
	data, err := parsedABI.Pack("balanceOf", walletAddress)
	if err != nil {
		return false, errors.Wrap(err, "构造调用数据失败")
	}

	// 调用合约
	result, err := client.CallContract(l.ctx, ethereum.CallMsg{
		From: walletAddress,
		To:   &tokenAddress,
		Data: data,
		Gas:  uint64(100000),
	}, nil)
	if err != nil {
		return false, errors.Wrap(err, "调用合约失败")
	}

	// 检查返回结果是否为空
	if len(result) == 0 {
		return false, errors.New("合约返回空结果")
	}

	// 解析返回的余额
	var balance *big.Int
	err = parsedABI.UnpackIntoInterface(&balance, "balanceOf", result)
	if err != nil {
		return false, errors.Wrap(err, "解析余额失败")
	}

	// 计算所需总金额（转账金额 + 预估gas费用）
	requiredAmount := new(big.Int).Add(big.NewInt(record.RecordHzbCoin), big.NewInt(1000000)) // 预估gas费用
	return balance.Cmp(requiredAmount) >= 0, nil
}

// 验证状态转换
func (l *BlockchainApprovalExecuteLogic) validateStatusTransition(oldStatus, newStatus int64) error {
	validTransitions := map[int64][]int64{
		bizblockchain.AuditStatus:  {bizblockchain.DoneStatus, bizblockchain.RejectStatus},
		bizblockchain.DoneStatus:   {bizblockchain.TransferSuccess},
		bizblockchain.RejectStatus: {bizblockchain.TransferFailed},
	}

	if validStates, exists := validTransitions[oldStatus]; exists {
		for _, validState := range validStates {
			if validState == newStatus {
				return nil
			}
		}
	}
	return fmt.Errorf("非法的状态转换: %d -> %d", oldStatus, newStatus)
}

// 处理转账错误
func (l *BlockchainApprovalExecuteLogic) handleTransferError(record *ent.HzBlockchainWalletRecord, err error) {
	record.RecordStatus = bizblockchain.RejectStatus
	record.RecordTransferStatus = bizblockchain.TransferFailed
	record.RecordRejectCause = fmt.Sprintf("转账失败[%s]:%v", err.Error(), err)
	record.RecordRejectTime = time.Now().Unix()
	logx.Errorf("转账失败,记录ID:%d,错误:%v", record.ID, err)
}

func (l *BlockchainApprovalExecuteLogic) BlockchainApprovalExecute(req *types.BlockchainApprovalExecuteRequest) (resp *types.BlockchainApprovalExecuteResponse, err error) {
	// 参数验证
	if req.RecordNumber == "" {
		return nil, errors.New("交易流水号不能为空")
	}
	if req.Operational != 1 && req.Operational != 2 {
		return nil, errors.New("无效的操作类型")
	}
	if req.Operational == 2 && req.Reason == "" {
		return nil, errors.New("拒绝时必须提供原因")
	}

	logx.Infof("开始处理审批请求，记录号：%s,操作类型:%d", req.RecordNumber, req.Operational)

	// 获取区块链钱包记录DAO
	blockchainWalletRecordDao := blockchain.NewBlockchainWalletRecordDao(l.ctx, l.svcCtx)

	// 构建查询条件
	cond := make([]predicate.HzBlockchainWalletRecord, 0)
	cond = append(cond, hzblockchainwalletrecord.RecordNumberEQ(req.RecordNumber))
	cond = append(cond, hzblockchainwalletrecord.RecordStatusEQ(bizblockchain.AuditStatus))

	// 查询记录
	records, _, err := blockchainWalletRecordDao.GetBlockchainWalletRecords(cond, 1, 1)
	if err != nil {
		logx.Errorf("查询区块链钱包记录失败，记录号：%s,错误:%v", req.RecordNumber, err)
		return nil, errors.Wrap(err, "查询区块链钱包记录失败")
	}
	if len(records) == 0 {
		return nil, errors.New("记录不存在或状态不正确")
	}
	record := records[0]

	// 设置事务超时
	ctx, cancel := context.WithTimeout(l.ctx, 30*time.Second)
	defer cancel()

	// 开启事务
	tx, err := l.svcCtx.Orm.Tx(ctx)
	if err != nil {
		return nil, errors.Wrap(err, "开启事务失败")
	}
	defer func() {
		if err != nil {
			if rollbackErr := tx.Rollback(); rollbackErr != nil {
				logx.Errorf("回滚事务失败，原始错误：%v,回滚错误:%v", err, rollbackErr)
			}
		}
	}()

	// 处理操作
	switch req.Operational {
	case bizblockchain.AcceptApplication: // approve
		// 检查钱包余额是否足够
		hasEnoughBalance, err := l.checkWalletBalance(record)
		if err != nil {
			logx.Errorf("检查钱包余额失败，记录号：%s,错误:%v", req.RecordNumber, err)
			return nil, errors.Wrap(err, "检查钱包余额失败")
		}

		if !hasEnoughBalance {
			// 余额不足，自动拒绝
			if err := l.validateStatusTransition(record.RecordStatus, bizblockchain.RejectStatus); err != nil {
				return nil, err
			}
			record.RecordStatus = bizblockchain.RejectStatus
			record.RecordTransferStatus = bizblockchain.TransferFailed
			record.RecordRejectCause = "转账钱包余额不足，自动拒绝"
			record.RecordRejectTime = time.Now().Unix()
			logx.Infof("转账钱包余额不足，自动拒绝记录：%s", req.RecordNumber)
		} else {
			// 余额充足，审批通过
			if err := l.validateStatusTransition(record.RecordStatus, bizblockchain.DoneStatus); err != nil {
				return nil, err
			}
			record.RecordStatus = bizblockchain.DoneStatus
			record.RecordTransferStatus = bizblockchain.TransferProcessing
			record.RecordPassTime = time.Now().Unix()

			// 调用转账接口
			transferLogic := NewBlockchainWalletTransferLogic(l.ctx, l.svcCtx)
			transferReq := &types.BlockchainWalletTransferRequest{
				RecordID: int64(record.ID),
			}
			transferResp, err := transferLogic.BlockchainWalletTransfer(transferReq)
			if err != nil {
				l.handleTransferError(record, err)
			} else if !transferResp.Success {
				record.RecordStatus = bizblockchain.RejectStatus
				record.RecordTransferStatus = bizblockchain.TransferFailed
				record.RecordRejectCause = transferResp.Message
				record.RecordRejectTime = time.Now().Unix()
			} else {
				record.RecordTransferStatus = bizblockchain.TransferSuccess
				record.RecordTransferCompleteTime = time.Now().Unix()
			}
		}
	case bizblockchain.RefuseApplication: // reject
		// 审批拒绝
		if err := l.validateStatusTransition(record.RecordStatus, bizblockchain.RejectStatus); err != nil {
			return nil, err
		}
		record.RecordStatus = bizblockchain.RejectStatus
		record.RecordTransferStatus = bizblockchain.TransferFailed
		record.RecordRejectCause = req.Reason
		record.RecordRejectTime = time.Now().Unix()

		// 回滚用户积分
		if record.UserID <= 0 {
			// 获取用户积分钱包
			integralWalletDao := integral.NewIntegralWalletDao(l.ctx, l.svcCtx)
			walletCond := make([]predicate.HzIntegralWallet, 0)
			walletCond = append(walletCond, hzintegralwallet.UserIDEQ(record.UserID))
			integralWallet, err := integralWalletDao.GetIntegralWalletOne(walletCond)
			if err != nil {
				logx.Errorf("获取用户积分钱包失败，用户ID：%d,错误:%v", record.UserID, err)
				return nil, errors.Wrap(err, "获取用户积分钱包失败")
			}

			rollbackAmount := record.RecordIntegralAmount

			// 更新积分钱包余额
			integralWallet.WalletActiveBalance += record.RecordIntegralAmount
			if _, err := tx.HzIntegralWallet.UpdateOne(integralWallet).
				SetWalletActiveBalance(integralWallet.WalletActiveBalance).
				Save(l.ctx); err != nil {
				logx.Errorf("更新积分钱包余额失败，用户ID：%d,错误:%v", record.UserID, err)
				return nil, errors.Wrap(err, "更新积分钱包余额失败")
			}

			// 创建积分回滚记录
			now := time.Now().Unix()
			recordNumber := mycommon.GenerateRandomNumber(18)
			if err := tx.HzIntegralWalletRecord.Create().
				SetRecordTitle("区块链转账回滚").
				SetRecordNumber(recordNumber).
				SetRecordAmount(rollbackAmount).
				SetRecordStatus(bizintegral.Income).
				SetRecordType(2). // 数字货币兑换类型
				SetRecordActiveBalance(integralWallet.WalletActiveBalance).
				SetRecordFreezeBalance(integralWallet.WalletFreezeBalance).
				SetUserID(record.UserID).
				SetIntegralWalletID(int64(integralWallet.ID)).
				SetCreatedAt(now).
				SetUpdateAt(now).
				Exec(l.ctx); err != nil {
				logx.Errorf("创建积分回滚记录失败，用户ID：%d,错误:%v", record.UserID, err)
				return nil, errors.Wrap(err, "创建积分回滚记录失败")
			}
		}
	default:
		return nil, errors.New("不支持的操作类型")
	}

	// 更新记录
	updateTime := time.Now().Unix()
	record.UpdateAt = updateTime
	_, err = tx.HzBlockchainWalletRecord.UpdateOne(record).
		SetRecordStatus(record.RecordStatus).
		SetRecordTransferStatus(record.RecordTransferStatus).
		SetRecordRejectCause(record.RecordRejectCause).
		SetRecordRejectTime(record.RecordRejectTime).
		SetRecordPassTime(record.RecordPassTime).
		SetRecordTransferCompleteTime(record.RecordTransferCompleteTime).
		SetUpdateAt(updateTime).
		Save(l.ctx)
	if err != nil {
		return nil, errors.Wrap(err, "更新记录失败")
	}

	// 提交事务
	if err = tx.Commit(); err != nil {
		return nil, errors.Wrap(err, "提交事务失败")
	}

	logx.Infof("审批处理完成，记录号：%s,最终状态:%d", req.RecordNumber, record.RecordStatus)

	return &types.BlockchainApprovalExecuteResponse{
		Status: "success",
	}, nil
}
